#ifndef DO_VEC2_H
#define DO_VEC2_H

/*
Copyright (c) 2014, ebaklund
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, 
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, 
   this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, 
   this list of conditions and the following disclaimer in the documentation 
   and/or other materials provided with the distribution.

3. Neither the name of the <ORGANIZATION> nor the names of its contributors 
   may be used to endorse or promote products derived from this software 
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/


#include <Do/Real.h>

#include <type_traits>


namespace Do
{
  template<typename T>
  struct Vec2
  {
    // Public object data
    T x, y;

    T* ptr() {
      static_assert (
        std::is_standard_layout<Vec2<T>>::value, 
        "Must be stadard layout for this function to be legal." );

      return reinterpret_cast<T*>(&x);
    }

    T const* ptr() const {
      static_assert (
        std::is_standard_layout<Vec2<T>>::value, 
        "Must be stadard layout for this function to be legal." );

      return reinterpret_cast<T const*>(&x);
    }

    // Public class methods
    static T cross(T ax, T ay, T bx, T by) { 
      return ax*by - ay*bx; 
    }
    
    static T cross(Vec2<T> const& a, Vec2<T> const& b) { 
      return Vec2<T>::cross(a.x, a.y, b.x, b.y); 
    }

    static T dot(T ax, T ay, T bx, T by) { 
      return ax*bx + ay*by; 
    }

    static T dot(Vec2<T> const& a, Vec2<T> const& b) { 
      return dot(a.x, a.y, b.x, b.y); 
    }

    static T quad(T x, T y) { 
      return dot(x, y, x, y); 
    }

    template<typename U=T>
    static typename std::enable_if<std::is_floating_point<U>::value, U>::
      
    type length(U x, U y) { 
      return static_cast<U>(::sqrt(quad(x, y))); 
    }

    // Public foundation methods

    Vec2() = default;

    Vec2(T bx, T by) : x(bx), y(by) { 
    }

    void operator = (std::tuple<T, T> const& tuple) {
      x = tuple[0];
      y = tuple[1];
    }

    // Public properties

    std::tuple<T,T> tuple() const {
      return std::tuple<T,T>(x, y);
    }

    T quad() const { 
      return quad(x, y); 
    }

    template<typename U=T>
    typename std::enable_if<std::is_floating_point<U>::value, U>::
      
    type length() const { 
      return length(x, y); 
    }

    // Public predicates

    template<typename U=T>
    typename std::enable_if<std::is_floating_point<U>::value, bool>::
      
    // operator == is not needed since this cals is a POD
    type eq(Vec2<U> const& other) {
      return 
        Do::Real::eq(x, other.x) && 
        Do::Real::eq(y, other.y);
    }

    template<typename U=T>
    typename std::enable_if<std::is_floating_point<U>::value, bool>::
      
    // operator != is not needed since this cals is a POD
    type neq(Vec2<U> const& other) {
      return 
        Do::Real::neq(x, other.x) || 
        Do::Real::neq(y, other.y);
    }

    template<typename U=T>
    typename std::enable_if<std::is_floating_point<U>::value, bool>::
      
    type eqb(Vec2<U> const& other, uint8_t bits_slack) {
      return 
        Do::Real::eqb(x, other.x, bits_slack) && 
        Do::Real::eqb(y, other.y, bits_slack);
    }

    template<typename U=T>
    typename std::enable_if<std::is_floating_point<U>::value, bool>::
      
    type neqb(Vec2<U> const& other, uint8_t bits_slack) {
      return 
        Do::Real::neqb(x, other.x, bits_slack) ||
        Do::Real::neqb(y, other.y, bits_slack);
    }

    // Public modifiers

    Vec2<T>& operator *= (T v) { 
      x *= v;
      y *= v;
      return *this;
    }

    Vec2<T>& operator /= (T v) { 
      x /= v;
      y /= v;
      return *this;
    }

    Vec2<T>& operator *= (Vec2<T> const& v) { 
      x *= v.x;
      y *= v.y;
      return *this;
    }

    Vec2<T>& operator /= (Vec2<T> const& v) { 
      x /= v.x;
      y /= v.y;
      return *this;
    }

    Vec2<T>& operator += (Vec2<T> const& v) { 
      x += v.x;
      y += v.y;
      return *this;
    }

    Vec2<T>& operator -= (Vec2<T> const& v) { 
      x -= v.x;
      y -= v.y;
      return *this;
    }

    Vec2<T>& operator - () { 
      x = -x;
      y = -y;
      return *this;
    }

    template<typename U=T>
    typename std::enable_if<std::is_floating_point<U>::value, Vec2<U>&>::
      
    type normalize() {
      return (*this) /= length();
    }

  }; // Vec2

  typedef Vec2<float>  Vec2f;
  typedef Vec2<double> Vec2d;

} // Do


template<typename T>
inline Do::Vec2<T> operator * (Do::Vec2<T> const& a, T b) {
  return Do::Vec2<T>(a.x*b, a.y*b);
}

template<typename T>
inline Do::Vec2<T> operator * (T a, Do::Vec2<T> const& b) {
  return Do::Vec2<T>(a*b.x, a*b.y);
}

template<typename T>
inline Do::Vec2<T> operator * (Do::Vec2<T> const& a, Do::Vec2<T> const& b) {
  return Do::Vec2<T>(a.x*b.x, a.y*b.y);
}


template<typename T>
inline Do::Vec2<T> operator / (Do::Vec2<T> const& a, T b) {
  return Do::Vec2<T>(a.x/b, a.y/b);
}

template<typename T>
inline Do::Vec2<T> operator / (T a, Do::Vec2<T> const& b) {
  return Do::Vec2<T>(a/b.x, a/b.y);
}

template<typename T>
inline Do::Vec2<T> operator / (Do::Vec2<T> const& a, Do::Vec2<T> const& b) {
  return Do::Vec2<T>(a.x/b.x, a.y/b.y);
}


template<typename T>
inline Do::Vec2<T> operator + (Do::Vec2<T> const& a, T b) {
  return Do::Vec2<T>(a.x+b, a.y+b);
}

template<typename T>
inline Do::Vec2<T> operator + (T a, Do::Vec2<T> const& b) {
  return Do::Vec2<T>(a+b.x, a+b.y);
}

template<typename T>
inline Do::Vec2<T> operator + (Do::Vec2<T> const& a, Do::Vec2<T> const& b) {
  return Do::Vec2<T>(a.x+b.x, a.y+b.y);
}


template<typename T>
inline Do::Vec2<T> operator - (Do::Vec2<T> const& a, T b) {
  return Do::Vec2<T>(a.x-b, a.y-b);
}

template<typename T>
inline Do::Vec2<T> operator - (T a, Do::Vec2<T> const& b) {
  return Do::Vec2<T>(a-b.x, a-b.y);
}

template<typename T>
inline Do::Vec2<T> operator - (Do::Vec2<T> const& a, Do::Vec2<T> const& b) {
  return Do::Vec2<T>(a.x-b.x, a.y-b.y);
}

#endif // DO_VEC2_H
